package util;

import java.util.ArrayList;
import java.util.List;

public class ConsoleTableUtil {
    public static final boolean PRINT_HEADER_TRUE = true, PRINT_HEADER_FALSE = false;// 公共值，固定待引用
    private static final char CT_SEPARATOR = '|', CT_SEPARATOR_LINE = '‐', CT_BREAK = '\n', CT_SPACE = '\0', CT_TAB = '\t';// @-涉及的字符
    private static final int margin = 1, javaTLen = 18; // 左间距（大于0）；Java中\t占字节数（改无意义）。
    private int bodyRow = 0; // @-内容下标=一行一行重置；
    private List<Integer> maxLens = new ArrayList<Integer>();

    private static String printChar(char c, int len) {
        StringBuilder buf = new StringBuilder();
        for (int i = 0; i < len; i++) {
            buf.append(c);
        }
        return buf.toString();
    }

    public static boolean isChar(char c) { // 判断中英文 (中：false,英：true)
        int k = 0x80;
        return c / k == 0;
    }

    public static int length(String s) { // 得到字符串占位，中文2位，英文1位
        if (s == null)
            return 0;
        char[] c = s.toCharArray();
        int len = 0;
        for (int i = 0; i < c.length; i++) {
            len++;
            if (!isChar(c[i])) {
                len++;
            }
        }
        return len;
    }

    private String checkValue(String value) {
        value = value == null ? "NULL" : value;
        if (value.length() > maxValueLenth + 3)
            return value.substring(0, maxValueLenth) + "...";
        return value;
    }

    private String appendByList(List<Object> list, int size) {
        if (size < 1)
            size = list.size();
        StringBuilder buf = new StringBuilder();
        for (int r = 0; r < size; r++) {
            Object value = list.get(r);
            int len = maxLens.get(r), vlen = length(value.toString());
            String vAppend = "";
            while (len - vlen >= javaTLen) {
                if (len - vlen == javaTLen)
                    vAppend += CT_SPACE;
                vAppend += CT_TAB;
                vlen += javaTLen;
            }
            buf.append(value);
            if ((len - vlen) % 8 != 0)
                buf.append(CT_TAB);
            buf.append(vAppend);
            if (r + 1 == size) {
                buf.append(CT_SEPARATOR).append(CT_TAB);
            }
        }
        return buf.toString();
    }

    private List<Object> header = new ArrayList<Object>();
    private int headerSize = 0;
    private List<List<Object>> body = new ArrayList<List<Object>>();
    private int bodySize = 0;
    private boolean printHeader = true;
    private int maxValueLenth = 50;

    public boolean isPrintHeader() {
        return printHeader;
    }

    public void setPrintHeader(boolean printHeader) {
        this.printHeader = printHeader;
    }

    public int getMaxValueLenth() {
        return maxValueLenth;
    }

    /**
     * TODO 设置列值最大长度，超过则用省略号代替（需大于0且在头与内容前面设置有效）
     *
     * @param maxValueLenth
     */
    public void setMaxValueLenth(int maxValueLenth) {
        if (maxValueLenth > 0 && headerSize + bodySize == 0)
            this.maxValueLenth = maxValueLenth;
    }

    public ConsoleTableUtil() {
    }

    public ConsoleTableUtil(boolean printHeader) {
        this.printHeader = printHeader;
    }

    public ConsoleTableUtil(boolean printHeader, int maxValueLenth) {
        this.printHeader = printHeader;
        this.maxValueLenth = maxValueLenth;
    }

    public ConsoleTableUtil appendHeader(Object value) {
        StringBuffer valBuf = new StringBuffer();
        valBuf.append(CT_SEPARATOR).append(printChar(CT_SPACE, margin));
        valBuf.append(checkValue(value.toString()));
        value = valBuf.toString();
        this.header.add(value);
        int len = maxLens.size() > headerSize ? maxLens.get(headerSize) : -1, vlen = length(value.toString());
        if (vlen % javaTLen == 0)
            vlen++;
        if (len < vlen && len == -1)
            maxLens.add(vlen);
        else if (len < vlen)
            maxLens.set(headerSize, vlen);
        headerSize = this.header.size();
        return this;
    }

    public ConsoleTableUtil appendBody(Object value) {
        StringBuffer valBuf = new StringBuffer();
        List<Object> body = null;
        if (bodyRow == 0)
            body = new ArrayList<Object>();
        else
            body = this.body.get(bodySize - 1);
        valBuf.append(CT_SEPARATOR).append(printChar(CT_SPACE, margin));
        valBuf.append(checkValue(value.toString()));
        value = valBuf.toString();
        body.add(value);
        int len = maxLens.size() > bodyRow ? maxLens.get(bodyRow) : -1, vlen = length(value.toString());
        if (vlen % javaTLen == 0)
            vlen++;
        if (len < vlen && len == -1)
            maxLens.add(vlen);
        else if (len < vlen)
            maxLens.set(bodyRow, vlen);
        if (bodyRow == 0)
            this.body.add(body);
        bodyRow++;
        bodySize = this.body.size();
        return this;
    }

    public void appendRow() {
        bodyRow = 0;
    }

    public String toString() {
        StringBuilder bufLine = new StringBuilder();
        StringBuilder bufHead = new StringBuilder();
        StringBuilder bufBody = new StringBuilder();
        // @-Init maxLens, need one:
        int bufLineLen = 0;
        for (int i = 0; i < maxLens.size(); i++) {
            int len = maxLens.get(i), chu = len % javaTLen;
            if (chu != 0)
                len += javaTLen - chu;
            maxLens.set(i, len);
            bufLineLen += len;
        }
        if (headerSize < 1)
            printHeader = PRINT_HEADER_FALSE;
        if (printHeader)
            bufHead.append(appendByList(header, headerSize));
        for (int i = 0; i < body.size(); i++) {
            bufBody.append(appendByList(body.get(i), 0));
            bufBody.append(CT_BREAK);
        }
        //bufLine.append(CT_SEPARATOR).append(printChar(CT_SEPARATOR_LINE, bufLineLen - 1)).append(CT_SEPARATOR).append(CT_TAB).append(CT_BREAK);
        bufBody.append(bufLine);
        if (printHeader) {
            bufLine.append(bufHead).append(CT_BREAK);
            //bufLine.append(CT_SEPARATOR).append(printChar(CT_SEPARATOR_LINE, bufLineLen - 1)).append(CT_SEPARATOR).append(CT_TAB).append(CT_BREAK);
        }
        return bufLine.append(bufBody).toString();
    }


    }

